
--------------------------------------------------------------------------
-- 银享卡
--#author:liuzhihong
--time:2022-04-18
--
--------------------------------------------------------------------------

-- 文件
local gg         = require("modules.business_welfare.welfare_gg")
local config     = require("modules.business_welfare.welfare_lua_config")
local uu         = require("modules.commhm.utils")
local ns_account = require("modules.business_welfare.welfare_bridge")
local log        = require("modules.commhm.log")
local t_yxcard   = require("modules.business_welfare.yxcard.t_yxcard")
local op_log     = require("modules.business_welfare.welfare_op_log")
local common_ssdb = require("modules.business_common.business_ssdb")
local code_conf   = require("modules.business_welfare.welfare_code")
local ns_config   = require("modules.business_welfare.welfare_config")
local common_uu   = require("modules.business_common.business_utils")
local business_account  = require("modules.business_common.business_account")
local http        = require("modules.business_common.utils.business_http")

-- 变量 开头定义优先级 系统库 > ngx > 自定义
local pairs      = pairs
local ipairs     = ipairs
local math       = math
local math_abs   = math.abs
local math_fmod  = math.fmod
local math_floor = math.floor
local next       = next
local table      = table
local table_insert = table.insert
local tonumber   = tonumber
local os         = os
local os_date    = os.date
local os_time    = os.time
local ngx = ngx
local ngx_time = ngx.time
local error_code = code_conf.error_code
local FOO = {
    _VERSION = '0.12.1',
}

local due_time = 157680000

-- !@埋点日志
local write_op_log = function(loginfo)
    if not loginfo then
        return
    end

    op_log.record("welfare_gift", loginfo.action, loginfo)
end


--[[
    tdata =
    {
        first_active =,
        last_notifytime = ,
        cardlst ={
                    {card_id= , card_type=, active_time=,  prize={time1, time2, time3}},
                    ...
                 }
    }
]]
-- !@ 获得银享卡数据
local get_data_dc = function(uin)
    local _uin = uin or ngx.ctx.m_params.uin
    if not ngx.ctx.card_info then
        --拉取变量
        local ret, data =  gg.private_hash_get_table_ssdb(_uin, _uin, common_ssdb.KEYS.YIN_XIANG_CARD)
        if ret == 0 and data then
            ngx.ctx.card_info = data
        elseif error_code.ERROR_NO_DATA == ret then
            ngx.ctx.card_info = {}
        else
            log.error("error::get_data_dc private_hash_get_table_ssdb failed ,ssdb_keyname =" .. common_ssdb.KEYS.YIN_XIANG_CARD)
        end
    end

    ngx.ctx.card_info.tdata = ngx.ctx.card_info.tdata or {}
    return ngx.ctx.card_info
end

-- !@ 获得银享卡数据
local get_data = function( uin )
    local data = get_data_dc(uin)
    local card_data = common_uu.table_copy(data.tdata or {} )
    card_data.cardlst = card_data.cardlst or {}
    return card_data
end


-- !@ 设置银享卡数据
local set_data = function( uin, card_data, bsave )
    local _uin = uin or ngx.ctx.m_params.uin
    ngx.ctx.card_info =  ngx.ctx.card_info or {}
    ngx.ctx.card_info.tdata = card_data

    if bsave then  --正式保存的时候删除无效的银享卡数据
         -- 删除过期数据
        FOO.remove_overdue_card(ngx.ctx.card_info.tdata)
        -- 删除完成了的银享卡数据
        FOO.remove_finish_card(ngx.ctx.card_info.tdata)
        return gg.private_hash_set_table_ssdb(_uin, _uin, common_ssdb.KEYS.YIN_XIANG_CARD, ngx.ctx.card_info)
    end

    return true
end

-- !@ 设置银享卡数据
local get_card_config = function(card_type)
    local tcfg = config.get_cfg("yin_xiang_card")
    if not tcfg then
        return
    end

    return tcfg[card_type]
end

-- !@ 获得银享卡实体卡配置
local get_card_cdk_config = function(card_id, apiid)
    apiid = apiid or ngx.ctx.m_params.apiid
    if not ngx.ctx.card_config then
        ngx.ctx.card_config = business_account.query_yxcard_cdk_config(card_id, apiid)
    end
    local err_code = {
        [4014] = error_code.CARD_CHANNEL_LIMIT,
        [4015] = error_code.CARD_INTERCEPT
    }
    log.day_list("test1234", uu.to_str({
        cfg = ngx.ctx.card_config,
        apiid = apiid
    }))

    if uu.tableIsEmpty(ngx.ctx.card_config) or not ngx.ctx.card_config.card_type then
        return false, err_code[ngx.ctx.card_config.ret or 0]
    end
    
    return true, ngx.ctx.card_config
end

-- !@银享卡已经完成[所有奖励已经领完]
local card_is_finished = function(card_info)
    local getnum = common_uu.table_getn(card_info.prize)
    local tcfg =  get_card_config(card_info.card_type)
    if not tcfg then
        return false
    end
    if getnum < tcfg.times then
        return false
    end

    return true
end

-- !@银享卡激活超过5年
local card_is_overdue = function(card_info)
    if math_abs( ngx_time() - card_info.active_time) < due_time then  --5年后删除卡
        return false
    end

    return true
end

-- !@ 获得本卡类型当前激活数量
local get_acive_num = function(uin, card_type)
    local tcfg =  get_card_config(card_type)
    if not tcfg then
        return 0
    end

    local count = 0
    local data = get_data(uin)
    for _, card in next, data.cardlst do
        if tcfg.id == card.card_type then
            count = count + 1
        end
    end
    return count
end


-- !@激活银享卡头像框
local open_frames  = function()
    local tcfg = config.get_cfg("HeadFrameDef")
    if not tcfg then
        log.day_list("welfare_yxcard", "get not headframedef config!!")
        return false
    end

    local  frame_ids = {}
    for frame_id, rcd in next, tcfg do
        if rcd.VipType == 2 then
            table_insert(frame_ids, frame_id)
        end
    end

    if not uu.tableIsEmpty(frame_ids) then
        local bresult = business_account.open_vip_head_frame(frame_ids)
        if not bresult then
            log.day_list("welfare_yxcard", "open_frames failed!", uu.table2json(frame_ids))
            return false
        end
    end

    return true
end

-- !@ 检查是否可以激活银享卡
local can_active_yxcard = function(uin, cardid)
    local data = get_data()
    if data.cardlst[cardid] then
        return false, error_code.REPEAT_ACTIVE_CARD
    end

    local bsuc, info = get_card_cdk_config(cardid)
    if not bsuc then
        return false, info or error_code.INVALID_CARD
    end

    local tcfg = get_card_config(info.card_type)
    if not tcfg then
        return false, error_code.ERROR_CONFIG
    end

    --  先判断卡是否已经被使用过
    local cond = {where = {card_id = cardid}}
    local res =  t_yxcard.select_record(cond, nil)
    if res ~= nil and uu.tableNotEmpty(res) then
        return false, error_code.CARD_USED
    end

    -- 判断银享卡类型激活次数
    if get_acive_num(uin, info.card_type) >= tcfg.num then
        return false, error_code.MAX_TIMES_LIMIT
    end

    return true
end

-- !@ 激活银享卡
local active_yxccard_do = function(uin, card_id)
    local bsuc, info = get_card_cdk_config(card_id)
    if not bsuc then
        return info or error_code.INVALID_CARD
    end
    local card_type = info.card_type
    local tcfg = get_card_config(card_type)
    if not tcfg then
        return error_code.ERROR_CONFIG
    end
    -- 先做使用卡处理
    local data_ = {
        card_id = card_id,
        card_type = card_type,
        uin = uin,
        rcd_time = os_date("%Y-%m-%d %H:%M:%S", ngx_time()),
    }

    local res = t_yxcard.insert_record({values = data_ })
    if not res then
        return error_code.USE_CARD_FAILED
    end

    -- 激活效果处理
    local bfirstbuy = 0
    local data = get_data(uin)
    if not data.first_active then
        data.first_active = ngx_time()
        bfirstbuy = 1
        --  首次激活银享卡处理[增加头像框/勋章]
        local bOK, code = FOO.first_active_handle( uin )
        if not bOK then
            return code
        end
    end

    data.cardlst[card_id] = {card_id = card_id, card_type = card_type, active_time=ngx_time(),  prize={}}

    local bNotify = FOO.notify_prize_mail()
    if bNotify then
        data.last_notifytime = ngx_time()
    end

    if not set_data(uin, data, true) then
        log.day_list("welfare_yxcard", "active_yxccard_do  use_card failed!", uu.to_str(data))
        return error_code.USE_CARD_FAILED
    end

    -- 记录op_log日志
    local loginfo = {action="welfare_active_yxccard", card_id = card_id, card_type = card_type, bfirstbuy = bfirstbuy, config_id = info.config_id}
    write_op_log(loginfo)
    loginfo.carddata = data
    loginfo.uin = uin
    log.day_list("welfare_yxcard", "active_yxccard_do------", uu.to_str(loginfo))
    return error_code.COMMON_SUCCESS, FOO.get_format_data(data.cardlst[card_id])
end

-- !@根据某个已购买的银享卡获得当月可领的奖励
local get_card_prize = function(card_info)
    -- card_info : {card_id= , card_type=, active_time=,  prize={time1, time2, time3}}
    local card_type = card_info.card_type
    local active_time = card_info.active_time
    local cur_time = ngx_time()
    if not active_time or active_time <= 0 or cur_time < active_time  then
        return {}, 0
    end

    local tcfg = get_card_config(card_type)
    if not tcfg then
        return {}, 0
    end

    local tprize_lst = {}
    local real_times = FOO.get_prizetimes(card_info)
    for _ = 1, real_times do
        for _, item in next, tcfg.prize do
            tprize_lst[item.itemid] = tprize_lst[item.itemid] or 0
            tprize_lst[item.itemid] = tprize_lst[item.itemid] + item.itemnum
        end
    end

    return tprize_lst, real_times
end

-- !@获得玩家 某类型银享卡当前可以领取的奖励
local get_all_prize = function(uin, card_type)
    local t_prize = {}
    local t_card_times = {}
    local data = get_data(uin)
    for _, card_info in next, data.cardlst do
        if card_info.card_type == card_type then
            local tprize_lst, times = get_card_prize(card_info)
            for item_id , item_num in next, tprize_lst do
                t_prize[item_id] = t_prize[item_id] or 0
                t_prize[item_id] = t_prize[item_id] + item_num
            end
            t_card_times[card_info.card_id] = times  -- 卡片独立算次数
        end
    end

    return t_prize, t_card_times
end

-- !@ 首次激活处理
FOO.first_active_handle = function( uin )
    local data = get_data(uin)

    -- 首次激活,开启头像框
    local bopenOk = open_frames()
    if not bopenOk then
        log.day_list("welfare_yxcard", "active_yxccard_do  open_frames failed!", uu.to_str(data))
        return false, error_code.ADD_FRAME_FAILED
    end

    return true
end


-- !@ 领奖完成的卡删除
FOO.remove_finish_card = function( card_data )
    local delete_lst = {}
    local tmp_tab = common_uu.table_copy(card_data)
    for _, card_info in next, tmp_tab.cardlst do
         if card_is_finished(card_info) then
            card_data.cardlst[card_info.card_id] = nil
            table.insert(delete_lst, card_info.card_id)
         end
    end

    if uu.tableNotEmpty(delete_lst) then
        local logdata = {}
        logdata.uin = ngx.ctx.m_params.uin
        logdata.action = ngx.ctx.m_params.act or ngx.ctx.m_params.cmd
        logdata.old_data = tmp_tab
        logdata.new_data = card_data
        logdata.delete_lst = delete_lst
        log.day_list("welfare_yxcard", "remove_finish_card  delete finished cards: ", uu.to_str(logdata))
    end
end

-- !@ 过期的卡删除  激活5年后
FOO.remove_overdue_card = function(card_data)
    local delete_lst = {}
    local tmp_tab = common_uu.table_copy(card_data)
    for _, card_info in next, tmp_tab.cardlst do
         if card_is_overdue(card_info) then  --5年后删除卡
            card_data.cardlst[card_info.card_id] = nil
            table.insert(delete_lst, card_info.card_id)
         end
    end

    if uu.tableNotEmpty(delete_lst) then
        local logdata = {}
        logdata.uin = ngx.ctx.m_params.uin
        logdata.action = ngx.ctx.m_params.act or ngx.ctx.m_params.cmd
        logdata.old_data = tmp_tab
        logdata.new_data = card_data
        logdata.delete_lst = delete_lst
        log.day_list("welfare_yxcard", "remove_overdue_card  delete overdue cards: ", uu.to_str(logdata))
    end

    return card_data
end


-- !@ 根据当前卡片信息获得下次奖励时间
FOO.get_next_prizetime = function( a_card_info )
    if not a_card_info then
        return -1
    end

    local active_time = a_card_info.active_time
    if card_is_finished(a_card_info) then
        return -1
    end

    if card_is_overdue(a_card_info) then
        return -1
    end

    local tcfg = get_card_config(a_card_info.card_type)
    if not tcfg then
        return -1
    end

    local cur_time = ngx_time()
    local nprize_times = common_uu.table_getn(a_card_info.prize)
    local diff_m = common_uu.time_diff_by_month(cur_time, active_time)
    local prize_times = diff_m + 1
    if prize_times > nprize_times then  -- 有奖励可领取状态
        return 0
    end

    -- 返回下个月的1号0点
    local cur_date = os_date("*t")
    local year = cur_date.year
    local month = cur_date.month + 1
    if month > 12 then
        year = year + 1
        month = math_fmod( month, 12)
    end

    cur_date.year = year
    cur_date.month = month
    cur_date.day  = 1
    cur_date.hour = 0
    cur_date.min  = 0
    cur_date.sec  = 0
    return os_time(cur_date)
end

-- !@ 获得卡片的奖励次数信息
FOO.get_prizetimes = function(a_card_info)
    if not a_card_info then
        return 0
    end

    local tcfg = get_card_config(a_card_info.card_type)
    if not tcfg then
        return 0
    end

    if card_is_finished(a_card_info) then
        return 0
    end

    if card_is_overdue(a_card_info) then
        return 0
    end

    local cur_time = ngx_time()
    local diff_m = common_uu.time_diff_by_month(cur_time, a_card_info.active_time)
    local prize_times = diff_m + 1
    prize_times =  (prize_times >= tcfg.times) and tcfg.times or prize_times
    local has_times = common_uu.table_getn(a_card_info.prize or {})
    local real_times =  prize_times - has_times
    real_times = real_times > 0 and real_times or 0
    local left_times = tcfg.times - has_times
    left_times = left_times > 0 and left_times or 0
    return real_times, left_times
end

-- !@ 将卡信息转化成客户端需要的格式
FOO.get_format_data = function( a_card_info )
    if not a_card_info or uu.tableIsEmpty(a_card_info) then
        return
    end

    if card_is_overdue(a_card_info) then
        return
    end

    if card_is_finished(a_card_info) then
        return
    end

    local tmp_tab = common_uu.table_copy(a_card_info)
    local format_data = {card_id = tmp_tab.card_id, card_type = tmp_tab.card_type,  active_time = tmp_tab.active_time}
    format_data.next_begintime = FOO.get_next_prizetime(tmp_tab)
    format_data.cur_times, format_data.lefttimes = FOO.get_prizetimes(tmp_tab)
    return format_data
end

-- !@ 激活银享卡
FOO.welfare_active_yxccard = function()
    local uin = tonumber(ngx.ctx.m_params.uin or 0)
    local cardid = ngx.ctx.m_params.card_id

    -- 先判断是否可以激活
    local bcan, code = can_active_yxcard(uin, cardid)
    if not bcan then
        return code
    end

    return active_yxccard_do(uin, cardid)
end


-- !@ 获得银享卡数据 主要提供给客户端直接使用
FOO.welfare_get_yxcdata = function()
    local uin = tonumber(ngx.ctx.m_params.uin or 0)
    local data = get_data(uin)
    local cardlst = data.cardlst or {}
    local tdata = {}
    for _, cardinfo in next, cardlst do
        local tcard_data = FOO.get_format_data(cardinfo)
        if tcard_data then
            table_insert(tdata, tcard_data)
        end
    end

    local bNotify = FOO.notify_prize_mail()
    if bNotify then
        data.last_notifytime = ngx_time()
        set_data(uin, data, true)
    end

    return error_code.COMMON_SUCCESS,  tdata
end

-- !@ 获得银享卡ssdb数据
FOO.welfare_get_ssdb_yxcdata = function()
    local uin = tonumber(ngx.ctx.m_params.uin or 0)
    local data = get_data(uin)

    return error_code.COMMON_SUCCESS,  data
end

-- !@ 获得奖励
FOO.welfare_get_prize = function()
    local uin = tonumber(ngx.ctx.m_params.uin or 0)
    local card_type = tonumber(ngx.ctx.m_params.card_type or 0)
    if uin == 0 or not card_type == 0 then
        return error_code.ERROR_PARAM
    end

    local logtab = {}
    logtab.uin = uin
    local data = get_data(uin)
    if not data or uu.tableIsEmpty(data) then
        return error_code.NO_ACTIVE_CARD
    end

    logtab.before_data = common_uu.table_copy(data)  --发奖前数据
    local nowtime = ngx_time()
    local t_prize, t_times = get_all_prize(uin, card_type)
    --先设置奖励次数
    for card_id, times in next, t_times do
        local card_info = data.cardlst[card_id]
        if not card_info then
            log.day_list("welfare_yxcard", "calc times failed!! uin=", uin,  uu.to_str(data))
            return error_code.CALC_TIMES_FAILED
        end
        for _ = 1, times do
            table_insert(card_info.prize, nowtime)
        end
    end

    -- 再设置玩家银享卡数据
    local bsuc = set_data(uin, data, true)
    if not bsuc then
        log.day_list("welfare_yxcard", "save data failed!! uin=", uin,  uu.to_str(data))
        return error_code.SAVE_DATA_FAILED
    end

    -- 最后添加奖励
    local items = {}  -- {{id=xx,num=xx},{id=xx,num=xx},...}
    for item_id, item_num in next, t_prize do
        table_insert(items, {id=item_id,num=item_num})
    end

    -- 有奖励则发放
    if common_uu.table_getn(items) > 0 then
       local  ret_ = business_account.add_item(uin, items, "yxcard_prize",  nil, "add_yxcard")
       if not  ret_ or ret_.return_code ~= "SUCCESS" then
            return error_code.ERROR_ADD_ITEM
       end
    end

    logtab.after_data = data  --发奖前数据
    logtab.prize = t_prize
    log.day_list("welfare_yxcard", "welfare_get_prize", "logtab:", uu.to_str(logtab))

    local ret_data = {}
    ret_data.prize_type = card_type
    ret_data.cardlst = {}
    ret_data.prizelst = items
    for _, card_info in next, (data.cardlst or {}) do
        local format_data = FOO.get_format_data(card_info)
        if format_data then
            table_insert(ret_data.cardlst, format_data)
        end
    end

    return error_code.COMMON_SUCCESS, ret_data
end


-- !@ 奖励提醒邮件
FOO.notify_prize_mail = function()
    local uin = tonumber(ngx.ctx.m_params.uin or 0)
    local data = get_data(uin)
    if not data or uu.tableIsEmpty(data) then
        return false
    end

    local tcfg = config.get_cfg("yxcard_notify_mail")
    if not tcfg then
        return false
    end

    local nowtime = ngx_time()
    local notifytime = data.last_notifytime or 0
    local diff_m = common_uu.time_diff_by_month(nowtime, notifytime)
    if diff_m <= 0 then
        return false
    end

    local bhasprize = false
    for _, card_info in next, (data.cardlst or {}) do
        local tmp_tab = common_uu.table_copy(card_info)
        local prize_time = FOO.get_next_prizetime(tmp_tab)
        if prize_time == 0 or prize_time > 0 and prize_time < nowtime then
            bhasprize = true
            break
        end
    end

    if not bhasprize then
        return false
    end

    -- 发送邮件
	local mail_data = {
		uin = uin,
        title = tcfg.title ,
        body = tcfg.body,
        reason = 'yxcard_prize_notify',
        jump_name = tcfg.jump_name,
        jump_to = tcfg.jump_to,
        type  = tcfg.type,
	}

	local ok, _ = business_account.send_system_mail(mail_data, uin)
	if not ok then
        log.day_list("welfare_yxcard", "notify_prize_mail----failed! uin =", uin)
        return false
	end

    return true
end

-- !@ 修改已购银享卡的激活时间
FOO.yxcard_alter_activetime = function( offset )
    local uin = tonumber(ngx.ctx.m_params.uin or 0)
    local _month = tonumber(offset or 0)
    if ns_config.debug ~= 1 then
		log.error("only test env can use yxcard_alter_activetime!!" ..uin)
		return gg.return_table( {error_code.COMMON_ERROR} )
	end

    if uin == 0 or not _month == 0 then
        return error_code.ERROR_PARAM
    end

    local data = get_data(uin)
    if not data or uu.tableIsEmpty(data) then
        return error_code.NO_ACTIVE_CARD
    end

    for _, card_info in next, (data.cardlst or {}) do
        local active_time = card_info.active_time
        local active_date = os_date("*t", active_time)
        local total_month = active_date.year * 12 + active_date.month

        total_month = total_month + _month
        local year = math_floor(total_month / 12)
        local month = math_fmod(total_month , 12)
        if month == 0 then
            year = year - 1
            month = 12
        end
        active_date.year = year
        active_date.month = month
        card_info.active_time = os_time(active_date)
    end

    set_data(uin, data, true)
    return error_code.COMMON_SUCCESS
end

-- !@ 修改已购银享卡的最近的邮件提醒时间
FOO.yxcard_alter_lastmailttime = function(offset)
    local uin = tonumber(ngx.ctx.m_params.uin or 0)
    local _month = tonumber(offset or 0)
    if ns_config.debug ~= 1 then
		log.error("only test env can yxcard_alter_firsttime!!" ..uin)
		return gg.return_table( {error_code.COMMON_ERROR} )
	end

    if uin == 0 or not _month == 0 then
        return error_code.ERROR_PARAM
    end

    local data = get_data(uin)
    if not data or uu.tableIsEmpty(data) then
        return error_code.NO_ACTIVE_CARD
    end

    local active_time = data.last_notifytime or 0
    if active_time == 0 then
        return error_code.COMMON_SUCCESS
    end

    local active_date = os_date("*t", active_time)
    local total_month = active_date.year * 12 + active_date.month

    total_month = total_month + _month
    local year = math_floor(total_month / 12)
    local month = math_fmod(total_month , 12)
    if month == 0 then
        year = year - 1
        month = 12
    end
    active_date.year = year
    active_date.month = month
    data.last_notifytime = os_time(active_date)

    set_data(uin, data, true)
    return error_code.COMMON_SUCCESS
end


return FOO

